/*-------------------------------------------------------------------------
 *
 * oid.c
 *	  内置类型 Oid 的函数 ... 还包括 oidvector。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/oid.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <ctype.h>
#include <limits.h>

#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "nodes/value.h"
#include "utils/array.h"
#include "utils/builtins.h"


#define OidVectorSize(n)	(offsetof(oidvector, values) + (n) * sizeof(Oid))


/*****************************************************************************
 *	 用户 I/O 例程														 *
 *****************************************************************************/

static Oid fc_oidin_subr(const char *fc_s, char **fc_endloc)
{
	unsigned long fc_cvt;
	char	   *fc_endptr;
	Oid			fc_result;

	if (*fc_s == '\0')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"oid", fc_s)));

	errno = 0;
	fc_cvt = strtoul(fc_s, &fc_endptr, 10);

	/*
	 * strtoul() 通常只设置 ERANGE。 在某些系统上，它可能还设置
	 * EINVAL，这意味着它无法解析输入字符串。这在所有平台上通过
	 * 第二个 "if" 一致处理。
	 */
	if (errno && errno != ERANGE && errno != EINVAL)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"oid", fc_s)));

	if (fc_endptr == fc_s && *fc_s != '\0')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"oid", fc_s)));

	if (errno == ERANGE)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("value \"%s\" is out of range for type %s",
						fc_s, "oid")));

	if (fc_endloc)
	{
		/* 调用者希望处理字符串的其余部分 */
		*fc_endloc = fc_endptr;
	}
	else
	{
		/* 仅允许数字后面的空白字符 */
		while (*fc_endptr && isspace((unsigned char) *fc_endptr))
			fc_endptr++;
		if (*fc_endptr)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							"oid", fc_s)));
	}

	fc_result = (Oid) fc_cvt;

	/*
	 * 应对无符号长整型可能比 Oid 宽的可能性，在这种情况下
	 * strtoul 将不会对某些超出 Oid 范围的值引发错误。
	 *
	 * 为了向后兼容，我们希望接受以负号给出的输入，
	 * 因此在有符号或无符号扩展到长整型后，如果输入值匹配
	 * 则允许该输入值。
	 *
	 * 为确保在 32 位和 64 位平台上结果一致，确保
	 * 错误消息与 strtoul() 返回 ERANGE 时相同。
	 */
#if OID_MAX != ULONG_MAX
	if (fc_cvt != (unsigned long) fc_result &&
		fc_cvt != (unsigned long) ((int) fc_result))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("value \"%s\" is out of range for type %s",
						fc_s, "oid")));
#endif

	return fc_result;
}

Datum oidin(PG_FUNCTION_ARGS)
{
	char	   *fc_s = PG_GETARG_CSTRING(0);
	Oid			fc_result;

	fc_result = fc_oidin_subr(fc_s, NULL);
	PG_RETURN_OID(fc_result);
}

Datum oidout(PG_FUNCTION_ARGS)
{
	Oid			fc_o = PG_GETARG_OID(0);
	char	   *fc_result = (char *) palloc(12);

	snprintf(fc_result, 12, "%u", fc_o);
	PG_RETURN_CSTRING(fc_result);
}

/*
 *		oidrecv			- 将外部二进制格式转换为 oid
 */
Datum oidrecv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

	PG_RETURN_OID((Oid) pq_getmsgint(fc_buf, sizeof(Oid)));
}

/*
 *		oidsend			- 将 oid 转换为二进制格式
 */
Datum oidsend(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1 = PG_GETARG_OID(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendint32(&fc_buf, fc_arg1);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/*
 * 根据 Oids 的原始数组构造 oidvector
 *
 * 如果 oids 为 NULL，则调用者必须随后填充值[]
 */
oidvector *
buildoidvector(const Oid *fc_oids, int fc_n)
{
	oidvector  *fc_result;

	fc_result = (oidvector *) palloc0(OidVectorSize(fc_n));

	if (fc_n > 0 && fc_oids)
		memcpy(fc_result->values, fc_oids, fc_n * sizeof(Oid));

	/* 
	 * 附加标准数组头。 出于历史原因，我们将索引
	 * 下边界设置为0而不是1。
	 */
	SET_VARSIZE(fc_result, OidVectorSize(fc_n));
	fc_result->ndim = 1;
	fc_result->dataoffset = 0;		/* 从不包含null */
	fc_result->elemtype = OIDOID;
	fc_result->dim1 = fc_n;
	fc_result->lbound1 = 0;

	return fc_result;
}

/*
 *		oidvectorin			- 将 "num num ..." 转换为内部形式
 */
Datum oidvectorin(PG_FUNCTION_ARGS)
{
	char	   *fc_oidString = PG_GETARG_CSTRING(0);
	oidvector  *fc_result;
	int			fc_nalloc;
	int			fc_n;

	fc_nalloc = 32;				/* 任意初始大小猜测 */
	fc_result = (oidvector *) palloc0(OidVectorSize(fc_nalloc));

	for (fc_n = 0;; fc_n++)
	{
		while (*fc_oidString && isspace((unsigned char) *fc_oidString))
			fc_oidString++;
		if (*fc_oidString == '\0')
			break;

		if (fc_n >= fc_nalloc)
		{
			fc_nalloc *= 2;
			fc_result = (oidvector *) repalloc(fc_result, OidVectorSize(fc_nalloc));
		}

		fc_result->values[fc_n] = fc_oidin_subr(fc_oidString, &fc_oidString);
	}

	SET_VARSIZE(fc_result, OidVectorSize(fc_n));
	fc_result->ndim = 1;
	fc_result->dataoffset = 0;		/* 从不包含null */
	fc_result->elemtype = OIDOID;
	fc_result->dim1 = fc_n;
	fc_result->lbound1 = 0;

	PG_RETURN_POINTER(fc_result);
}

/*
 *		oidvectorout - 将内部形式转换为 "num num ..."
 */
Datum oidvectorout(PG_FUNCTION_ARGS)
{
	oidvector  *fc_oidArray = (oidvector *) PG_GETARG_POINTER(0);
	int			fc_num,
				fc_nnums = fc_oidArray->dim1;
	char	   *fc_rp;
	char	   *fc_result;

	/* 假设符号，10 位数，' ' */
	fc_rp = fc_result = (char *) palloc(fc_nnums * 12 + 1);
	for (fc_num = 0; fc_num < fc_nnums; fc_num++)
	{
		if (fc_num != 0)
			*fc_rp++ = ' ';
		sprintf(fc_rp, "%u", fc_oidArray->values[fc_num]);
		while (*++fc_rp != '\0')
			;
	}
	*fc_rp = '\0';
	PG_RETURN_CSTRING(fc_result);
}

/*
 *		oidvectorrecv			- 将外部二进制格式转换为 oidvector
 */
Datum oidvectorrecv(PG_FUNCTION_ARGS)
{
	LOCAL_FCINFO(fc_locfcinfo, 3);
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	oidvector  *fc_result;

	/*
	 * 通常应该使用 DirectFunctionCall3 调用 array_recv()，但是
	 * 这不起作用，因为 array_recv 想要使用
	 * fcinfo->flinfo->fn_extra 缓存一些数据。所以我们需要传递自己的 flinfo
	 * 参数。
	 */
	InitFunctionCallInfoData(*fc_locfcinfo, fcinfo->flinfo, 3,
							 InvalidOid, NULL, NULL);

	fc_locfcinfo->args[0].value = PointerGetDatum(fc_buf);
	fc_locfcinfo->args[0].isnull = false;
	fc_locfcinfo->args[1].value = ObjectIdGetDatum(OIDOID);
	fc_locfcinfo->args[1].isnull = false;
	fc_locfcinfo->args[2].value = Int32GetDatum(-1);
	fc_locfcinfo->args[2].isnull = false;

	fc_result = (oidvector *) DatumGetPointer(array_recv(fc_locfcinfo));

	Assert(!fc_locfcinfo->isnull);

	/* 合理性检查：oidvector 必须是 1-D，0 基的，无空值 */
	if (ARR_NDIM(fc_result) != 1 ||
		ARR_HASNULL(fc_result) ||
		ARR_ELEMTYPE(fc_result) != OIDOID ||
		ARR_LBOUND(fc_result)[0] != 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
				 errmsg("invalid oidvector data")));

	PG_RETURN_POINTER(fc_result);
}

/*
 *		oidvectorsend			- 将 oidvector 转换为二进制格式
 */
Datum oidvectorsend(PG_FUNCTION_ARGS)
{
	return array_send(fcinfo);
}

/*
 *		oidparse				- 从 ICONST/FCONST 节点获取 OID
 */
Oid oidparse(Node *fc_node)
{
	switch (nodeTag(fc_node))
	{
		case T_Integer:
			return intVal(fc_node);
		case T_Float:

			/*
			 * 对于int4而言，值过大将通过词法分析器表示为浮点常量。
			 * 如果它们是有效的OID字符串，则接受这些值。
			 */
			return fc_oidin_subr(castNode(Float, fc_node)->fval, NULL);
		default:
			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(fc_node));
	}
	return InvalidOid;			/* 保持编译器安静 */
}

/* Oids 的 qsort 比较函数 */
int oid_cmp(const void *fc_p1, const void *fc_p2)
{
	Oid			fc_v1 = *((const Oid *) fc_p1);
	Oid			fc_v2 = *((const Oid *) fc_p2);

	if (fc_v1 < fc_v2)
		return -1;
	if (fc_v1 > fc_v2)
		return 1;
	return 0;
}


/*****************************************************************************
 *	 公共例程														 *
 *****************************************************************************/

Datum oideq(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1 = PG_GETARG_OID(0);
	Oid			fc_arg2 = PG_GETARG_OID(1);

	PG_RETURN_BOOL(fc_arg1 == fc_arg2);
}

Datum oidne(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1 = PG_GETARG_OID(0);
	Oid			fc_arg2 = PG_GETARG_OID(1);

	PG_RETURN_BOOL(fc_arg1 != fc_arg2);
}

Datum oidlt(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1 = PG_GETARG_OID(0);
	Oid			fc_arg2 = PG_GETARG_OID(1);

	PG_RETURN_BOOL(fc_arg1 < fc_arg2);
}

Datum oidle(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1 = PG_GETARG_OID(0);
	Oid			fc_arg2 = PG_GETARG_OID(1);

	PG_RETURN_BOOL(fc_arg1 <= fc_arg2);
}

Datum oidge(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1 = PG_GETARG_OID(0);
	Oid			fc_arg2 = PG_GETARG_OID(1);

	PG_RETURN_BOOL(fc_arg1 >= fc_arg2);
}

Datum oidgt(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1 = PG_GETARG_OID(0);
	Oid			fc_arg2 = PG_GETARG_OID(1);

	PG_RETURN_BOOL(fc_arg1 > fc_arg2);
}

Datum oidlarger(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1 = PG_GETARG_OID(0);
	Oid			fc_arg2 = PG_GETARG_OID(1);

	PG_RETURN_OID((fc_arg1 > fc_arg2) ? fc_arg1 : fc_arg2);
}

Datum oidsmaller(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1 = PG_GETARG_OID(0);
	Oid			fc_arg2 = PG_GETARG_OID(1);

	PG_RETURN_OID((fc_arg1 < fc_arg2) ? fc_arg1 : fc_arg2);
}

Datum oidvectoreq(PG_FUNCTION_ARGS)
{
	int32		fc_cmp = DatumGetInt32(btoidvectorcmp(fcinfo));

	PG_RETURN_BOOL(fc_cmp == 0);
}

Datum oidvectorne(PG_FUNCTION_ARGS)
{
	int32		fc_cmp = DatumGetInt32(btoidvectorcmp(fcinfo));

	PG_RETURN_BOOL(fc_cmp != 0);
}

Datum oidvectorlt(PG_FUNCTION_ARGS)
{
	int32		fc_cmp = DatumGetInt32(btoidvectorcmp(fcinfo));

	PG_RETURN_BOOL(fc_cmp < 0);
}

Datum oidvectorle(PG_FUNCTION_ARGS)
{
	int32		fc_cmp = DatumGetInt32(btoidvectorcmp(fcinfo));

	PG_RETURN_BOOL(fc_cmp <= 0);
}

Datum oidvectorge(PG_FUNCTION_ARGS)
{
	int32		fc_cmp = DatumGetInt32(btoidvectorcmp(fcinfo));

	PG_RETURN_BOOL(fc_cmp >= 0);
}

Datum oidvectorgt(PG_FUNCTION_ARGS)
{
	int32		fc_cmp = DatumGetInt32(btoidvectorcmp(fcinfo));

	PG_RETURN_BOOL(fc_cmp > 0);
}
